#include "polynom.h"
#include <algorithm>

std::vector<std::string> split_terms(const std::string &s)
{
    std::vector<std::string> terms;
    std::string term = "";
    int i = 0;

    while (i < s.length())
    {
        char c = s[i];
        if (c == ' ')
        {
            ++i;
            continue;
        }
        if (c == '+' || c == '-')
        {
            if (!term.empty())
            {
                terms.push_back(term);
                term = "";
            }
        }
        term += c;
        ++i;
    }
    if (!term.empty())
    {
        terms.push_back(term);
    }

    return terms;
}

Polynomial::Polynomial() : degree(0)
{
    poly.push_back(Term());
}

Polynomial::Polynomial(int coeff) : degree(0)
{
    poly.push_back(Term(coeff));
}

Polynomial::Polynomial(const Term &t) : degree(t.getDeg())
{
    if (t.getCoeff() != 0)
    {
        poly.push_back(t);
    }
    else
    {
        poly.push_back(Term(0, 0));
    }
}

Polynomial::Polynomial(const std::string &str) : degree(0)
{
    std::vector<std::string> terms = split_terms(str);
    for (const auto &term : terms)
    {
        *this = *this + Term(term);
    }
}

Polynomial::Polynomial(const Polynomial &p) : poly(p.poly), degree(p.degree) {}

Polynomial &Polynomial::operator=(const Polynomial &p)
{
    if (this != &p)
    {
        poly = p.poly;
        degree = p.degree;
    }
    return *this;
}

std::ostream &operator<<(std::ostream &out, const Polynomial &p)
{
    bool isEmpty = true;
    for (int i = 0; i < p.poly.size(); ++i)
    {
        if (p.poly[i].getCoeff() != 0)
        {
            isEmpty = false;

            if (i > 0)
            {
                if (p.poly[i].getCoeff() > 0)
                {
                    out << " + ";
                }
                else
                {
                    out << " - ";
                }
            }

            if (p.poly[i].getDeg() == 0 || abs(p.poly[i].getCoeff()) != 1)
            {
                out << abs(p.poly[i].getCoeff());
            }

            if (p.poly[i].getDeg() > 0)
            {
                out << "x";
                if (p.poly[i].getDeg() > 1)
                {
                    out << "^" << p.poly[i].getDeg();
                }
            }
        }
    }

    if (isEmpty)
    {
        out << "0";
    }

    return out;
}

void Polynomial::sortTerms()
{
    std::sort(poly.begin(), poly.end(), [](const Term &a, const Term &b)
              { return a.getDeg() > b.getDeg(); });
}

Polynomial operator+(const Polynomial &p, const Term &t)
{
    Polynomial temp(p);
    bool flag = true;
    for (int i = 0; i < temp.poly.size(); ++i)
    {
        if (temp.poly[i].getDeg() == t.getDeg())
        {
            temp.poly[i].setCoeff(temp.poly[i].getCoeff() + t.getCoeff());
            flag = false;
        }
    }
    if (flag)
    {
        temp.poly.push_back(t);
    }
    temp.sortTerms();
    return temp;
}

std::istream &operator>>(std::istream &in, Polynomial &p)
{
    std::string str, term = "";
    getline(std::cin, str);
    int i = 0;
    bool first = true;
    while (i < str.length())
    {
        char c = str[i];
        if (c == ' ')
        {
            ++i;
            continue;
        }
        if (c == '+' || c == '-')
        {
            if (!term.empty() && first)
            {
                p.poly.push_back(Term(term));
                term.clear();
            }
            else if (!term.empty())
            {
                p = p + Term(term);
            }
        }
        term += c;
        ++i;
    }
    if (!term.empty() && first)
    {
        p.poly.push_back(Term(term));
        term.clear();
    }
    else if (!term.empty())
    {
        p = p + Term(term);
    }

    return in;
}

Polynomial operator+(const Polynomial &p1, const Polynomial &p2)
{
    Polynomial temp(p1);
    for (int i = 0; i < p2.poly.size(); ++i)
    {
        temp = temp + p2.poly[i];
    }
    temp.sortTerms();
    return temp;
}

Polynomial &Polynomial::operator+=(const Polynomial &p)
{
    *this = *this + p;
    return *this;
}

Polynomial operator*(const Polynomial &p, const Term &t)
{
    Polynomial temp(p);
    for (int i = 0; i < temp.poly.size(); ++i)
    {
        temp.poly[i].setCoeff(p.poly[i].getCoeff() * t.getCoeff());
        temp.poly[i].setDeg(p.poly[i].getDeg() + t.getDeg());
    }
    return temp;
}

Polynomial operator*(const Polynomial &p1, const Polynomial &p2)
{
    Polynomial temp;
    for (int i = 0; i < p2.poly.size(); ++i)
    {
        temp += p1 * p2.poly[i];
    }
    temp.sortTerms();
    return temp;
}

Polynomial &Polynomial::operator*=(const Polynomial &p)
{
    *this = *this * p;
    return *this;
}
